module klang.编译.扫描工具.扫描工具_;
import 全局;
import std.functional: ·fd = toDelegate;
import ··枚举值_ = klang.编译.类型.枚举值.枚举值_;
import klang.编译.类型.枚举值.枚举值_: TK, SK, 字符_, 令牌标志_;
import klang.编译.注册.注册_: 获取注册的前端通过前端目标;
import klang.编译.类型.类型_: 行和列_, 前端接口_, 设置扫描_, 文本范围_, 注释范围_;
import klang.编译.语法节点.节点_: 节点_, 源文件_, 节点数组_, positionIsSynthesized;
import klang.编译.核心.核心_: 整数_, 最小, 创建数组, 二分搜索, 比较器比较值, 是空白或换行, 是换行类的, 是空白单行字符;
import 编译数字 = 编译数字.编译;
import klang.编译.诊断集.诊断集_: 诊断集_;
import klang.编译.诊断.诊断_: 诊断消息_;
dstring 多行注释头;
dstring 多行注释尾;
dstring 单行注释头;
TK 当前目标;
// All conflict markers consist of the same character repeated seven times.  If it is
// a <<<<<<< or >>>>>>> marker then it is also followed by a space.
long 合并冲突标记长度;
正则_!(dchar) 指令杂项正则;
dstring 保存多行注释头;
dstring 保存多行注释尾;
dstring 保存单行注释头;
TK 保存当前目标;
// All conflict markers consist of the same character repeated seven times.  If it is
// a <<<<<<< or >>>>>>> marker then it is also followed by a space.
long 保存合并冲突标记长度;
正则_!(dchar) 保存指令杂项正则;
void 设置扫描工具环境(TK 目标, dstring 单行头, dstring 多行头, dstring 多行尾) {
    if (目标 != .当前目标) {
        .保存原扫描工具环境();
        .多行注释头 = 多行头;
        .多行注释尾 = 多行尾;
        .单行注释头 = 单行头;
        .合并冲突标记长度 = "<<<<<<<"d.length;
        .指令杂项正则 = ·regex(r"^#!.*"d, "g"c);
    }
}
void 保存原扫描工具环境() {
    .保存多行注释头 = .多行注释头;
    .保存多行注释尾 = .多行注释尾;
    .保存单行注释头 = .单行注释头;
    .保存当前目标 = .当前目标;
}
void 恢复原扫描工具环境(TK 原目标, TK 目标, bool 强制 = false) {
    if ((原目标 == .保存当前目标 && 目标 == .当前目标) || 强制) {
        .多行注释头 = .保存多行注释头;
        .多行注释尾 = .保存多行注释尾;
        .单行注释头 = .保存单行注释头;
        .当前目标 = .保存当前目标;
    }
}
TK 获取扫描工具当前目标() {
    return .当前目标;
}
long 获取行和列的位置_1(源文件_ 源码, long 行, long 列) {
    return .计算行和列的位置(.获取行开始组(源码), 行, 列);
}
long 获取行和列的位置_2(源文件_ 源码, long 行, long 列, bool 允许编辑) {
    return .计算行和列的位置(.获取行开始组(源码), 行, 列, 源码.内容, 允许编辑);
}
long 获取行和列的位置_3(源文件_ 源码, long 行, long 列, dstring 调试文本, bool 允许编辑) {
    return .计算行和列的位置(.获取行开始组(源码), 行, 列, 调试文本, 允许编辑);
}
/* @internal */
long 计算行和列的位置(long[] 行开始组, long 行, long 列, dstring 调试文本 = ··null!(dstring), bool 允许编辑 = ··null!(bool)) {
    if (行 < 0 || 行 >= 行开始组.length) {
        if (允许编辑) {
            // Clamp line to nearest allowable value
            行 = 行 < 0 ? 0 : 行 >= 行开始组.length ? 行开始组.length - 1 : 行;
        }
        else {
            assert(false, "行或列的参数大小错误"c);
            assert(0);
        }
    }
    long 结果 = 行开始组[行] + 列;
    if (允许编辑) {
        // Clamp to nearest allowable values to allow the underlying to be edited without crashing (accuracy is lost, instead)
        // TODO: Somehow track edits between file as it was during the creation of 源码rcemap we have and the current file and
        // apply them to the computed position to improve accuracy
        long 位置 = 行 + 1;
        if (位置 < 行开始组.length && 结果 > 行开始组[位置]) {
            return 行开始组[位置];
        }
        else if ((调试文本 !is ··null!(dstring)) && 调试文本 != ""d && 结果 > 调试文本.length) {
            return 调试文本.length;
        }
        return 结果;
    }
    if (行 < 行开始组.length - 1) {
        assert(结果 < 行开始组[行 + 1], "是最后一行"c);
    }
    else if ((调试文本 !is ··null!(dstring)) && 调试文本 != ""d) {
        assert(结果 <= 调试文本.length, "结果必须小于调试文本的长度"c); // Allow single character overflow for trailing newline
    }
    return 结果;
}
/* @internal */
/**
 * We assume the first line starts at position 0 and 'position' is non-negative.
 */
行和列_ 计算位置的行和列(long[] 行开始组, long 位置, long 偏移 = ··null!(long)) {
    long 行位置 = 二分搜索!(long, long)(行开始组, 位置, (整数_ k) { return k; }, cast(··枚举值_.比较结果_ delegate(long, long))(·fd(&比较器比较值)), 偏移);
    if (行位置 < 0) {
        // If the actual position was not found,
        // the binary search returns the 2's-complement of the next line start
        // e.g. if the line starts at [5, 10, 23, 80] and the position requested was 20
        // then the search will return -2.
        //
        // We want the index of the previous line start, so we subtract 1.
        // Review 2's-complement if this is confusing.
        行位置 = ~行位置 - 1;
        assert(行位置 != -1, "position cannot precede the beginning of the file"c);
    }
    行和列_ ···tempS·0·_1;
    {
        ···tempS·0·_1.行 = 行位置;
        ···tempS·0·_1.列 = 位置 - 行开始组[行位置];
    }
    return ···tempS·0·_1;
}
long[] 获取行开始组(源文件_ 源码) {
    if (源码.行词典 is ··null!(long[])) {
        if (源码.目标 != .当前目标) {
            前端接口_ 前端 = 获取注册的前端通过前端目标(源码.目标);
            设置扫描_ 设置扫描 = 前端.获取扫描设置();
            .设置扫描工具环境(设置扫描.获取语言目标(), 设置扫描.单行注释头(), 设置扫描.多行注释头(), 设置扫描.多行注释尾());
        }
        源码.行词典 = .计算行开始组(源码.内容);
    }
    return 源码.行词典;
}
行和列_ 获取位置的行和列(源文件_ 源码, long 位置) {
    return .计算位置的行和列(.获取行开始组(源码), 位置);
}
/* @internal */
long[] 计算行开始组(dstring 内容) {
    long[] 结果 = [];
    int 开始 = 0;
    int 行开始 = 0;
    while(开始 < 内容.length) {
        immutable(dchar) ch = 内容[开始];
        ++开始;
        switch (ch) {
            case 字符_.回车符: {
                if (开始 < 内容.length && 内容[开始] == 字符_.换行符) {
                    ++开始;
                }
            }
            // 贯穿
            case 字符_.换行符: {
                结果 = .追加!(long)(结果, 行开始);
                行开始 = 开始;
                break;
            }
            default: {
                if (ch > 字符_.最大Ascii字符 && 是换行类的(ch)) {
                    结果 = .追加!(long)(结果, 行开始);
                    行开始 = 开始;
                }
                break;
            }
        }
    }
    结果 = .追加!(long)(结果, 行开始);
    return 结果;
}
bool 可以开始琐事(dstring 内容, long 开始) {
    // Keep in sync with skipTrivia
    immutable(dchar) ch = 内容[开始];
    switch (ch) {
        case 字符_.换行符:
        case 字符_.回车符:
        case 字符_.水平制表:
        case 字符_.垂直制表:
        case 字符_.分页:
        case 字符_.空格:
        case 字符_.斜杠:
        // starts of normal trivia
        // falls through
        case 字符_.左尖括号:
        case 字符_.或号:
        case 字符_.等号:
        case 字符_.右尖括号: {
            // Starts of conflict marker trivia
            return true;
        }
        case 字符_.井号: {
            // Only if its the beginning can we have #! trivia
            return 开始 == 0;
        }
        default: {
            return ch > 字符_.最大Ascii字符;
        }
    }
}
bool 位置是合成的(long pos) {
    // This is a fast way of testing the following conditions:
    //  pos == undefined || pos == null || isNaN(pos) || pos < 0;
    return !(pos >= 0);
}
/* @internal */
long 跳过杂项(dstring 内容, long 开始, bool 换行后停止 = ··null!(bool), bool 停止在注释 = false) {
    if (.位置是合成的(开始)) {
        return 开始;
    }
    // Keep in sync with couldStartTrivia
    while(true) {
        immutable(dchar) ch = 内容[开始];
        switch (ch) {
            case 字符_.回车符: {
                if (内容[开始 + 1] == 字符_.换行符) {
                    ++开始;
                }
            }
            // 贯穿
            case 字符_.换行符: {
                ++开始;
                if (换行后停止) {
                    return 开始;
                }
                continue;
            }
            case 字符_.水平制表:
            case 字符_.垂直制表:
            case 字符_.分页:
            case 字符_.空格: {
                ++开始;
                continue;
            }
            case 字符_.斜杠: {
                if (停止在注释) {
                    break;
                }
                if (内容[开始 + 1] == 字符_.斜杠) {
                    开始 += 2;
                    while(开始 < 内容.length) {
                        if (是换行类的(内容[开始])) {
                            break;
                        }
                        ++开始;
                    }
                    continue;
                }
                if (内容[开始 + 1] == 字符_.星号) {
                    开始 += 2;
                    while(开始 < 内容.length) {
                        if (内容[开始] == 字符_.星号 && 内容[开始 + 1] == 字符_.斜杠) {
                            开始 += 2;
                            break;
                        }
                        ++开始;
                    }
                    continue;
                }
                break;
            }
            case 字符_.左尖括号:
            case 字符_.或号:
            case 字符_.等号:
            case 字符_.右尖括号: {
                if (.是合并冲突标记(内容, 开始)) {
                    开始 = .扫描合并冲突标记(内容, 开始);
                    continue;
                }
                break;
            }
            case 字符_.井号: {
                if (开始 == 0 && .是指令杂项(内容, 开始)) {
                    开始 = .扫描指令杂项(内容, 开始);
                    continue;
                }
                break;
            }
            default: {
                if (ch > 字符_.最大Ascii字符 && (是空白或换行(ch))) {
                    ++开始;
                    continue;
                }
                break;
            }
        }
        return 开始;
    }
}
bool 是合并冲突标记(dstring 内容, long 开始) {
    assert(开始 >= 0, "开始位置不能 小于 0 "c);
    // Conflict markers must be at the start of a line.
    if (开始 == 0 || 是换行类的(内容[开始 - 1])) {
        immutable(dchar) ch = 内容[开始];
        if ((开始 + .合并冲突标记长度) < 内容.length) {
            {
                for (int i = 0; i < .合并冲突标记长度; ++i) {
                    if (内容[开始 + i] != ch) {
                        return false;
                    }
                }
            }
            return ch == 字符_.等号 || 内容[开始 + .合并冲突标记长度] == 字符_.空格;
        }
    }
    return false;
}
long 扫描合并冲突标记(dstring 内容, long 开始, void delegate(诊断消息_, long, long) 错误 = ··null!(void delegate(诊断消息_, long, long))) {
    if (错误 !is ··null!(void delegate(诊断消息_, long, long))) {
        错误(诊断集_.Merge_conflict_marker_encountered, 开始, .合并冲突标记长度);
    }
    immutable(dchar) ch = 内容[开始];
    size_t 长 = 内容.length;
    if (ch == 字符_.左尖括号 || ch == 字符_.右尖括号) {
        while(开始 < 长 && !是换行类的(内容[开始])) {
            ++开始;
        }
    }
    else {
        assert(ch == 字符_.或号 || ch == 字符_.等号, "字符错误"c);
        // Consume everything from the start of a ||||||| or ======= marker to the start
        // of the next ======= or >>>>>>> marker.
        while(开始 < 长) {
            immutable(dchar) 当前字符 = 内容[开始];
            if ((当前字符 == 字符_.等号 || 当前字符 == 字符_.右尖括号) && 当前字符 != ch && .是合并冲突标记(内容, 开始)) {
                break;
            }
            ++开始;
        }
    }
    return 开始;
}
/** Optionally, get the shebang */
dstring 获取指令(dstring 内容) {
    匹配结果_!(dstring) 匹配 = 指令杂项正则.p·匹配(内容);
    if (!匹配.empty()) {
        return 匹配.captures().front();
    }
    return ""d;
}
/*@internal*/
bool 是指令杂项(dstring 内容, long 开始) {
    // Shebangs check must only be done at the start of the file
    assert(开始 == 0, "'开始' 参数值错误, 指令杂项必须在代码最开始位置."c);
    return !指令杂项正则.p·匹配(内容).empty();
}
/*@internal*/
long 扫描指令杂项(dstring 内容, long 开始) {
    dstring 指令_ = 指令杂项正则.p·匹配(内容).captures().front();
    开始 = 开始 + 指令_.length;
    return 开始;
}
/**
 * Invokes a callback for each comment range following the provided position.
 *
 * Single-line comment ranges include the leading double-斜杠 characters but not the ending
 * line 跳出. Multi-line comment ranges include the leading 斜杠-asterisk and 尾部
 * asterisk-斜杠 characters.
 *
 * @param 缩减 If 真, accumulates the result of calling the callback in a fashion similar
 *      to reduceLeft. If 假, iteration stops when the callback returns a truthy value.
 * @param 内容 The source 内容 to 扫描.
 * @param 开始 The position at which to start scanning.
 * @param 尾部 If 假, whitespace is skipped until the first line 跳出 and comments
 *      between that location and the next token are returned. If 真, comments occurring
 *      between the given position and the next line 跳出 are returned.
 * @param cb The callback to execute as each comment range is encountered.
 * @param 状态 A 状态 value to pass to each iteration of the callback.
 * @param 最初 An 最初 value to pass when accumulating results (when "缩减" is 真).
 * @returns If "缩减" is 真, the accumulated value. If "缩减" is 假, the first truthy
 *      返回 value of the callback.
 */
void 迭代注释范围_2(dstring 内容, long 开始, bool 尾部, void delegate(long, long, SK, bool, long) cb, long 状态) {
    bool delegate(dchar, dstring) 是单行注释开始;
    bool delegate(dchar, dstring) 是多行注释开始;
    bool delegate(dchar, dstring) 是多行注释结尾;
    long 待处理开始;
    long 待处理结束;
    SK 待处理语法;
    bool 待处理有尾随新行;
    bool 待处理注释范围;
    size_t 内容长度;
    bool 收集;
    bool 是单行注释开始·func(dchar ch, dstring 内容) {
        long 索引;
        if (单行注释头.length == 1) {
            return ch == 单行注释头[0];
        }
        if (单行注释头.length == 2) {
            return ch == 单行注释头[0] && 开始 + 1 < 内容长度 && 内容[开始 + 1] == 单行注释头[1];
        }
        索引 = 开始 + 1;
        {
            for (int i = 0; i < 单行注释头.length; ++i, ++索引) {
                immutable(dchar) v = 单行注释头[i];
                if (ch != v) {
                    return false;
                }
                if (索引 < 内容.length) {
                    ch = (cast()(内容[索引]));
                }
                else {
                    return false;
                }
            }
        }
        return true;
    }
    是单行注释开始 = &是单行注释开始·func;
    bool 是多行注释开始·func(dchar ch, dstring 内容) {
        long 索引;
        if (多行注释头.length == 1) {
            return ch == 多行注释头[0];
        }
        if (多行注释头.length == 2) {
            return ch == 多行注释头[0] && 开始 + 1 < 内容长度 && 内容[开始 + 1] == 多行注释头[1];
        }
        索引 = 开始 + 1;
        {
            for (int i = 0; i < 多行注释头.length; ++i, ++索引) {
                immutable(dchar) v = 多行注释头[i];
                if (ch != v) {
                    return false;
                }
                if (索引 < 内容.length) {
                    ch = (cast()(内容[索引]));
                }
                else {
                    return false;
                }
            }
        }
        return true;
    }
    是多行注释开始 = &是多行注释开始·func;
    bool 是多行注释结尾·func(dchar ch, dstring 内容) {
        long 索引;
        if (多行注释尾.length == 1) {
            return ch == 多行注释尾[0];
        }
        if (多行注释尾.length == 2) {
            return ch == 多行注释尾[0] && 开始 + 1 < 内容长度 && 内容[开始 + 1] == 多行注释尾[1];
        }
        索引 = 开始 + 1;
        {
            for (int i = 0; i < 多行注释尾.length; ++i, ++索引) {
                immutable(dchar) v = 多行注释尾[i];
                if (ch != v) {
                    return false;
                }
                if (索引 < 内容.length) {
                    ch = (cast()(内容[索引]));
                }
                else {
                    return false;
                }
            }
        }
        return true;
    }
    是多行注释结尾 = &是多行注释结尾·func;
    if (内容 is ··null!(dstring)) {
        return;
    }
    待处理开始 = 0;
    待处理结束 = 0;
    待处理语法 = SK.单行注释;
    待处理有尾随新行 = false;
    待处理注释范围 = false;
    内容长度 = 内容.length;
    收集 = 尾部;
    if (开始 == 0) {
        收集 = true;
        dstring 指令_ = .获取指令(内容);
        if (指令_ !is ··null!(dstring)) {
            开始 = 指令_.length;
        }
    }
    扫描: while(开始 >= 0 && 开始 < 内容.length) {
        dchar ch = (cast()(内容[开始]));
        if (是多行注释开始(ch, 内容) || 是单行注释开始(ch, 内容)) {
            long 起点开始 = 开始;
            bool 有尾随新行 = false;
            SK 种类 = 是单行注释开始(ch, 内容) ? SK.单行注释 : SK.多行注释;
            if (是单行注释开始(ch, 内容)) {
                开始 += 单行注释头.length;
                while(开始 < 内容.length) {
                    if (是换行类的(内容[开始])) {
                        有尾随新行 = true;
                        break;
                    }
                    ++开始;
                }
            }
            else {
                while(开始 < 内容.length) {
                    if (是多行注释结尾(ch, 内容)) {
                        开始 += 多行注释尾.length;
                        break;
                    }
                    ++开始;
                }
            }
            if (收集) {
                if (待处理注释范围) {
                    cb(待处理开始, 待处理结束, 待处理语法, 待处理有尾随新行, 状态);
                }
                待处理开始 = 起点开始;
                待处理结束 = 开始;
                待处理语法 = 种类;
                待处理有尾随新行 = 有尾随新行;
                待处理注释范围 = true;
            }
            continue;
        }
        switch (ch) {
            case 字符_.回车符: {
                if (开始 + 1 < 内容.length && 内容[开始 + 1] == 字符_.换行符) {
                    ++开始;
                }
            }
            // 贯穿
            case 字符_.换行符: {
                ++开始;
                if (尾部) {
                    break 扫描;
                }
                收集 = true;
                if (待处理注释范围) {
                    待处理有尾随新行 = true;
                }
                continue;
            }
            case 字符_.水平制表:
            case 字符_.垂直制表:
            case 字符_.分页:
            case 字符_.空格:
            case 字符_.斜杠: {
                ++开始;
                continue;
            }
            default: {
                if (ch > 字符_.最大Ascii字符 && (是空白或换行(ch))) {
                    if (待处理注释范围 && 是换行类的(ch)) {
                        待处理有尾随新行 = true;
                    }
                    ++开始;
                    continue;
                }
                break 扫描;
            }
        }
    }
    if (待处理注释范围) {
        cb(待处理开始, 待处理结束, 待处理语法, 待处理有尾随新行, 状态);
    }
    return;
}
/**
 * Invokes a callback for each comment range following the provided position.
 *
 * Single-line comment ranges include the leading double-斜杠 characters but not the ending
 * line 跳出. Multi-line comment ranges include the leading 斜杠-asterisk and 尾部
 * asterisk-斜杠 characters.
 *
 * @param 缩减 If 真, accumulates the result of calling the callback in a fashion similar
 *      to reduceLeft. If 假, iteration stops when the callback returns a truthy value.
 * @param 内容 The source 内容 to 扫描.
 * @param 开始 The position at which to start scanning.
 * @param 尾部 If 假, whitespace is skipped until the first line 跳出 and comments
 *      between that location and the next token are returned. If 真, comments occurring
 *      between the given position and the next line 跳出 are returned.
 * @param cb The callback to execute as each comment range is encountered.
 * @param 状态 A 状态 value to pass to each iteration of the callback.
 * @param 最初 An 最初 value to pass when accumulating results (when "缩减" is 真).
 * @returns If "缩减" is 真, the accumulated value. If "缩减" is 假, the first truthy
 *      返回 value of the callback.
 */
U 迭代注释范围(T, U)(bool 缩减, dstring 内容, long 开始, bool 尾部, U delegate(long, long, SK, bool, T, U) cb, T 状态, U 最初) {
    bool delegate(dchar, dstring) 是单行注释开始;
    bool delegate(dchar, dstring) 是多行注释开始;
    bool delegate(dchar, dstring) 是多行注释结尾;
    long 待处理开始;
    long 待处理结束;
    SK 待处理语法;
    bool 待处理有尾随新行;
    bool 待处理注释范围;
    size_t 内容长度;
    bool 收集;
    U 累积的;
    bool 是单行注释开始·func(dchar ch, dstring 内容) {
        long 索引;
        if (单行注释头.length == 1) {
            return ch == 单行注释头[0];
        }
        if (单行注释头.length == 2) {
            return ch == 单行注释头[0] && 开始 + 1 < 内容长度 && 内容[开始 + 1] == 单行注释头[1];
        }
        索引 = 开始 + 1;
        {
            for (int i = 0; i < 单行注释头.length; ++i, ++索引) {
                immutable(dchar) v = 单行注释头[i];
                if (ch != v) {
                    return false;
                }
                if (索引 < 内容.length) {
                    ch = (cast()(内容[索引]));
                }
                else {
                    return false;
                }
            }
        }
        return true;
    }
    是单行注释开始 = &是单行注释开始·func;
    bool 是多行注释开始·func(dchar ch, dstring 内容) {
        long 索引;
        if (多行注释头.length == 1) {
            return ch == 多行注释头[0];
        }
        if (多行注释头.length == 2) {
            return ch == 多行注释头[0] && 开始 + 1 < 内容长度 && 内容[开始 + 1] == 多行注释头[1];
        }
        索引 = 开始 + 1;
        {
            for (int i = 0; i < 多行注释头.length; ++i, ++索引) {
                immutable(dchar) v = 多行注释头[i];
                if (ch != v) {
                    return false;
                }
                if (索引 < 内容.length) {
                    ch = (cast()(内容[索引]));
                }
                else {
                    return false;
                }
            }
        }
        return true;
    }
    是多行注释开始 = &是多行注释开始·func;
    bool 是多行注释结尾·func(dchar ch, dstring 内容) {
        long 索引;
        if (多行注释尾.length == 1) {
            return ch == 多行注释尾[0];
        }
        if (多行注释尾.length == 2) {
            return ch == 多行注释尾[0] && 开始 + 1 < 内容长度 && 内容[开始 + 1] == 多行注释尾[1];
        }
        索引 = 开始 + 1;
        {
            for (int i = 0; i < 多行注释尾.length; ++i, ++索引) {
                immutable(dchar) v = 多行注释尾[i];
                if (ch != v) {
                    return false;
                }
                if (索引 < 内容.length) {
                    ch = (cast()(内容[索引]));
                }
                else {
                    return false;
                }
            }
        }
        return true;
    }
    是多行注释结尾 = &是多行注释结尾·func;
    if (内容 is ··null!(dstring)) {
        return ··null!(U);
    }
    待处理开始 = 0;
    待处理结束 = 0;
    待处理语法 = SK.单行注释;
    待处理有尾随新行 = false;
    待处理注释范围 = false;
    内容长度 = 内容.length;
    收集 = 尾部;
    累积的 = 最初;
    if (开始 == 0) {
        收集 = true;
        dstring 指令_ = .获取指令(内容);
        if (指令_ !is ··null!(dstring)) {
            开始 = 指令_.length;
        }
    }
    扫描: while(开始 >= 0 && 开始 < 内容.length) {
        dchar ch = (cast()(内容[开始]));
        if (是多行注释开始(ch, 内容) || 是单行注释开始(ch, 内容)) {
            long 起点开始 = 开始;
            bool 有尾随新行 = false;
            SK 种类 = 是单行注释开始(ch, 内容) ? SK.单行注释 : SK.多行注释;
            if (是单行注释开始(ch, 内容)) {
                开始 += 单行注释头.length;
                while(开始 < 内容.length) {
                    if (是换行类的(内容[开始])) {
                        有尾随新行 = true;
                        break;
                    }
                    ++开始;
                }
            }
            else {
                while(开始 < 内容.length) {
                    if (是多行注释结尾(ch, 内容)) {
                        开始 += 多行注释尾.length;
                        break;
                    }
                    ++开始;
                }
            }
            if (收集) {
                if (待处理注释范围) {
                    累积的 = cb(待处理开始, 待处理结束, 待处理语法, 待处理有尾随新行, 状态, 累积的);
                    if (!缩减 && (累积的 !is ··null!(U))) {
                        // If we are not reducing and we have a truthy result, 返回 it.
                        return 累积的;
                    }
                }
                待处理开始 = 起点开始;
                待处理结束 = 开始;
                待处理语法 = 种类;
                待处理有尾随新行 = 有尾随新行;
                待处理注释范围 = true;
            }
            continue;
        }
        switch (ch) {
            case 字符_.回车符: {
                if (开始 + 1 < 内容.length && 内容[开始 + 1] == 字符_.换行符) {
                    ++开始;
                }
            }
            // 贯穿
            case 字符_.换行符: {
                ++开始;
                if (尾部) {
                    break 扫描;
                }
                收集 = true;
                if (待处理注释范围) {
                    待处理有尾随新行 = true;
                }
                continue;
            }
            case 字符_.水平制表:
            case 字符_.垂直制表:
            case 字符_.分页:
            case 字符_.空格:
            case 字符_.斜杠: {
                ++开始;
                continue;
            }
            default: {
                if (ch > 字符_.最大Ascii字符 && (是空白或换行(ch))) {
                    if (待处理注释范围 && 是换行类的(ch)) {
                        待处理有尾随新行 = true;
                    }
                    ++开始;
                    continue;
                }
                break 扫描;
            }
        }
    }
    if (待处理注释范围) {
        累积的 = cb(待处理开始, 待处理结束, 待处理语法, 待处理有尾随新行, 状态, 累积的);
    }
    return 累积的;
}
void 循环每个前方注释范围_1(dstring 内容, long 开始, void delegate(long, long, SK, bool, long) cb) {
    return .迭代注释范围_2(内容, 开始, /*尾部*/ false, cast(void delegate(long, long, SK, bool, long))(cb), 0);
}
void 循环每个前方注释范围_2(dstring 内容, long 开始, void delegate(long, long, SK, bool, long) cb, long 状态) {
    return .迭代注释范围_2(内容, 开始, /*尾部*/ false, cast(void delegate(long, long, SK, bool, long))(cb), 状态);
}
void 循环每个尾随注释范围_1(dstring 内容, long 开始, void delegate(long, long, SK, bool, long) cb) {
    return .迭代注释范围_2(内容, 开始, /*尾部*/ true, cast(void delegate(long, long, SK, bool, long))(cb), 0);
}
void 循环每个尾随注释范围_2(dstring 内容, long 开始, void delegate(long, long, SK, bool, long) cb, long 状态) {
    return .迭代注释范围_2(内容, 开始, /*尾部*/ true, cast(void delegate(long, long, SK, bool, long))(cb), 状态);
}
U 缩减每个前方注释范围(T, U)(dstring 内容, long 开始, U delegate(long, long, SK, bool, T, U) cb, T 状态, U 最初) {
    return .迭代注释范围!(T, U)(/*缩减*/ true, 内容, 开始, /*尾部*/ false, cast(U delegate(long, long, SK, bool, T, U))(cb), 状态, 最初);
}
U 缩减每个尾随注释范围(T, U)(dstring 内容, long 开始, U delegate(long, long, SK, bool, T, U) cb, T 状态, U 最初) {
    return .迭代注释范围!(T, U)(/*缩减*/ true, 内容, 开始, /*尾部*/ true, cast(U delegate(long, long, SK, bool, T, U))(cb), 状态, 最初);
}
注释范围_[] 添加到注释范围组(long 开始, long 结束, SK 种类, bool 有尾随新行, long _状态, 注释范围_[] 注释组) {
    if (注释组 is ··null!(注释范围_[])) {
        注释组 = [];
    }
    注释范围_ n = new 注释范围_(开始, 结束, 有尾随新行);
    n.种类 = 种类;
    注释组 = .追加!(注释范围_)(注释组, n);
    return 注释组;
}
注释范围_[] 获取前方注释范围组(dstring 内容, long 开始) {
    return .缩减每个前方注释范围!(long, 注释范围_[])(内容, 开始, cast(注释范围_[] delegate(long, long, SK, bool, long, 注释范围_[]))(·fd(&.添加到注释范围组)), /*状态*/ 0, /*最初*/ []);
}
注释范围_[] 获取尾随注释范围组(dstring 内容, long 开始) {
    return .缩减每个尾随注释范围!(long, 注释范围_[])(内容, 开始, cast(注释范围_[] delegate(long, long, SK, bool, long, 注释范围_[]))(·fd(&.添加到注释范围组)), /*状态*/ 0, /*最初*/ []);
}
/**
 * Creates a new 文本范围_ from the provided 开始 and 结束.
 *
 * @param 开始 The start position.
 * @param 结束 The 结束 position.
 */
文本范围_ createRange(long 开始, long 结束) {
    assert(结束 >= 开始 || 结束 == -1, "开始或结束数值错误"c);
    文本范围_ 结果 = new 文本范围_();
    结果.开始 = 开始;
    结果.结束 = 结束;
    return 结果;
}
/**
 * Creates a new 文本范围_ from a provided range with a new 结束 position.
 *
 * @param range A 文本范围_.
 * @param 结束 The new 结束 position.
 */
文本范围_ moveRangeEnd(文本范围_ range, long 结束) {
    return .createRange(range.开始, 结束);
}
/**
 * Creates a new 文本范围_ from a provided range with a new start position.
 *
 * @param range A 文本范围_.
 * @param 开始 The new Start position.
 */
文本范围_ moveRangePos(文本范围_ range, long 开始) {
    return .createRange(开始, range.结束);
}
/**
 * Determines whether a 文本范围_ has the same start and 结束 positions.
 *
 * @param range A 文本范围_.
 */
bool isCollapsedRange(文本范围_ range) {
    return range.开始 == range.结束;
}
bool rangeIsOnSingleLine(文本范围_ range, 源文件_ sourceFile) {
    return .rangeStartIsOnSameLineAsRangeEnd(range, range, sourceFile);
}
bool rangeStartPositionsAreOnSameLine(文本范围_ range1, 文本范围_ range2, 源文件_ sourceFile) {
    return .positionsAreOnSameLine(.getStartPositionOfRange(range1, sourceFile, /*includeComments*/ false), .getStartPositionOfRange(range2, sourceFile, /*includeComments*/ false), sourceFile);
}
bool rangeEndPositionsAreOnSameLine(文本范围_ range1, 文本范围_ range2, 源文件_ sourceFile) {
    return .positionsAreOnSameLine(range1.结束, range2.结束, sourceFile);
}
bool rangeStartIsOnSameLineAsRangeEnd(文本范围_ range1, 文本范围_ range2, 源文件_ sourceFile) {
    return .positionsAreOnSameLine(.getStartPositionOfRange(range1, sourceFile, /*includeComments*/ false), range2.结束, sourceFile);
}
bool rangeEndIsOnSameLineAsRangeStart(文本范围_ range1, 文本范围_ range2, 源文件_ sourceFile) {
    return .positionsAreOnSameLine(range1.结束, .getStartPositionOfRange(range2, sourceFile, /*includeComments*/ false), sourceFile);
}
long getLinesBetweenRangeEndAndRangeStart(文本范围_ range1, 文本范围_ range2, 源文件_ sourceFile, bool includeSecondRangeComments) {
    long range2Start = .getStartPositionOfRange(range2, sourceFile, includeSecondRangeComments);
    return .getLinesBetweenPositions(sourceFile, range1.结束, range2Start);
}
long getLinesBetweenRangeEndPositions(文本范围_ range1, 文本范围_ range2, 源文件_ sourceFile) {
    return .getLinesBetweenPositions(sourceFile, range1.结束, range2.结束);
}
bool isNodeArrayMultiLine(节点数组_!(节点_) list, 源文件_ sourceFile) {
    return !.positionsAreOnSameLine(list.开始, list.结束, sourceFile);
}
bool positionsAreOnSameLine(long pos1, long pos2, 源文件_ sourceFile) {
    return .getLinesBetweenPositions(sourceFile, pos1, pos2) == 0;
}
long getStartPositionOfRange(文本范围_ range, 源文件_ sourceFile, bool includeComments) {
    return positionIsSynthesized(range.开始) ? -1 : .跳过杂项(sourceFile.内容, range.开始, /*stopAfterLineBreak*/ false, includeComments);
}
long getLinesBetweenPositionAndPrecedingNonWhitespaceCharacter(long 开始, long stopPos, 源文件_ sourceFile, bool includeComments = ··null!(bool)) {
    long startPos = .跳过杂项(sourceFile.内容, 开始, /*stopAfterLineBreak*/ false, includeComments);
    long prevPos = .getPreviousNonWhitespacePosition(startPos, sourceFile, stopPos);
    long start = prevPos == 0 ? startPos : prevPos;
    return .getLinesBetweenPositions(sourceFile, start, startPos);
}
long getLinesBetweenPositionAndNextNonWhitespaceCharacter(long 开始, long stopPos, 源文件_ sourceFile, bool includeComments = ··null!(bool)) {
    long nextPos = .跳过杂项(sourceFile.内容, 开始, /*stopAfterLineBreak*/ false, includeComments);
    return .getLinesBetweenPositions(sourceFile, 开始, 最小(stopPos, nextPos));
}
long getPreviousNonWhitespacePosition(long 开始, 源文件_ sourceFile, long stopPos = 0) {
    while(--开始 > stopPos) {
        if (!是空白或换行(sourceFile.内容[开始])) {
            return 开始;
        }
    }
    return 0;
}
long getLinesBetweenPositions(源文件_ sourceFile, long pos1, long pos2) {
    if (pos1 == pos2) {
        return 0;
    }
    long[] lineStarts = .获取行开始组(sourceFile);
    long lower = 最小(pos1, pos2);
    bool isNegative = lower == pos2;
    long upper = isNegative ? pos1 : pos2;
    long lowerLine = .计算位置的行和列(lineStarts, lower).行;
    long upperLine = .计算位置的行和列(lineStarts, upper, lowerLine).行;
    return isNegative ? lowerLine - upperLine : upperLine - lowerLine;
}
unittest {
    void main() {
        dstring 内容 = "1234\n6789\r\n12"d;
        long[] 行组 = .计算行开始组(内容);
        assert(行组.length == 3, "`计算行开始组` 函数有错误"c);
        assert(行组[0] == 0, "`计算行开始组` 函数有错误"c);
        assert(行组[1] == 5, "`计算行开始组` 函数有错误"c);
        assert(行组[2] == 11, "`计算行开始组` 函数有错误"c);
    }
    main();
}
